#include "workspacewidget.h"
#include <QVBoxLayout>
#include <QGLWidget>
#include <QHBoxLayout>
#include <QCursor>
#include <QGuiApplication>
#include <QClipboard>
#include <QKeyEvent>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>

#include "../entities/unoboardentity.h"
#include "../systems/connectorsystem.h"
#include "../entities/whiteledentity.h"
#include "../entities/groundentity.h"
#include "../aurasim.h"

WorkspaceWidget::WorkspaceWidget(QWidget *parent) :
    QFrame(parent),
    mRenderView(QSharedPointer<RenderView>(new RenderView(this))),
    mScene(QSharedPointer<RenderScene>(new RenderScene(this))),
    mRightPanel(QSharedPointer<RightPanel>(new RightPanel(this))),
    mMinZoomFactor(5),
    mMaxZoomFactor(10000),
    mZoomFactor(250),
    mZoomBase(250)
{
    setObjectName("Workspace");
    initUI();
    initConnections();
}

void WorkspaceWidget::initUI()
{
    setFrameStyle(Sunken | StyledPanel);
    QHBoxLayout* mainLayout = new QHBoxLayout;

    mRightPanel->setFixedWidth(250);
    mRightPanel->setContentsMargins(0, 0, 0, 0);
    mainLayout->addWidget(mRightPanel.data());

    mainLayout->setContentsMargins(0, 0, 0, 0);
    mainLayout->addWidget(mRenderView.data());
    setLayout(mainLayout);
    setContentsMargins(0, 0, 0, 0);

    mRenderView->setBackgroundBrush(QColor(28, 29, 36));
    mRenderView->setScene(mScene.data());
    mScene->setStickyFocus(true);
    mRenderView->setContentsMargins(0, 0, 0, 0);
    mRenderView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
    mRenderView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);

    // enable opengl
    mRenderView->setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers)));
    QFile qssFile(":/qss/aurasim-default.qss");
    if(qssFile.open(QIODevice::ReadOnly)) {
        QString qss = qssFile.readAll();
        this->setStyleSheet(qss);
        qssFile.close();
        qDebug() << "theme loaded";
    }
    setMouseTracking(true);
}

void WorkspaceWidget::initConnections()
{
    connect(mRenderView.data(), &RenderView::reqeustZoomIn, [=]{
        mZoomFactor += 1;
        if(mZoomFactor > mMaxZoomFactor)
            mZoomFactor = mMaxZoomFactor;
        setupMatrix();
    });
    connect(mRenderView.data(), &RenderView::requestZoomOut, [=]{
        mZoomFactor -= 1;
        if(mZoomFactor < mMinZoomFactor)
            mZoomFactor = mMinZoomFactor;
        setupMatrix();
    });

    connect(Aurasim::entityManager().data(), &EntityManager::entityAdded, [=](Entity* entity){
        mScene->addItem(entity);
    });

    connect(mScene.data(), &QGraphicsScene::selectionChanged, [=]{
        Aurasim::app()->updateSelectedEntity(mScene->selectedItems());
    });
}

void WorkspaceWidget::setupMatrix()
{
    QMatrix matrix;
    qreal scale = qPow(2, (mZoomFactor - mZoomBase) / qreal(10));
    matrix.scale(scale, scale);
    mRenderView->setMatrix(matrix);
}

RenderView::RenderView(QWidget *parent): QGraphicsView(parent),
    mMousePressed(false)
{
    setRenderHint(QPainter::Antialiasing, true);
    setDragMode(QGraphicsView::RubberBandDrag);
    setOptimizationFlags(QGraphicsView::DontSavePainterState);
//    setViewportUpdateMode(QGraphicsView::SmartViewportUpdate);
    setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
    setMouseTracking(true);
    setAcceptDrops(true);
//    setRubberBandSelectionMode(Qt::IntersectsItemShape);
}

void RenderView::wheelEvent(QWheelEvent *event)
{
    if (event->modifiers() & Qt::ControlModifier){
        event->accept();
        if(event->angleDelta().y() > 0) {
            emit reqeustZoomIn();
        }
        if(event->angleDelta().y() < 0) {
            emit requestZoomOut();
        }
    } else {
        QGraphicsView::wheelEvent(event);
    }
}

void RenderView::mouseMoveEvent(QMouseEvent *e)
{
    if((e->pos() - mPressedPos).manhattanLength() > 20 && mMousePressed) {
        Aurasim::setMouseDraggingState(true);
    }
    QGraphicsView::mouseMoveEvent(e);
}

void RenderView::mousePressEvent(QMouseEvent *e)
{
//    if(e->modifiers() & Qt::ControlModifier){
//        setDragMode(QGraphicsView::ScrollHandDrag);
//    } else {
//        setDragMode(QGraphicsView::RubberBandDrag);
//    }
    if(e->button() == Qt::LeftButton) {
        mMousePressed = true;
    }
    mPressedPos = e->pos();
    QGraphicsView::mousePressEvent(e);
}

void RenderView::mouseReleaseEvent(QMouseEvent *e)
{
    mMousePressed = false;
//    setDragMode(QGraphicsView::RubberBandDrag);
    Aurasim::setMouseDraggingState(false);
    QGraphicsView::mouseReleaseEvent(e);
}

// TODO: (tommego): handle scene can't access drag drop event problem.
void RenderView::dragEnterEvent(QDragEnterEvent *event)
{
    if(event->mimeData()->hasFormat("text/plain"))
        event->setAccepted(true);
    else
        event->setAccepted(false);
}

RenderScene::RenderScene(QObject *parent):
    QGraphicsScene(parent)
{
}

void RenderScene::dropEvent(QGraphicsSceneDragDropEvent *event)
{
    // create entity on from drop datas
    QPoint pos = this->views().first()->mapFromGlobal(QCursor::pos());
    QString entityName = event->mimeData()->text();
    Entity* entity = Aurasim::newEntity(entityName);
    if(entity) {
        entity->setPos(this->views().first()->mapToScene(pos));
        Aurasim::entityManager()->addEntity(entity);
    }
}

void RenderScene::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
{
    if(event->mimeData()->hasFormat("text/plain")) {
        qDebug() << event << event->mimeData()->text();
        event->setAccepted(true);
    } else{
        event->setAccepted(false);
    }
}

void RenderScene::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
{
    Q_UNUSED(event)
}

void RenderScene::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
{
    Q_UNUSED(event)
}

void RenderScene::keyPressEvent(QKeyEvent *event)
{
    if(event->key() == Qt::Key_Delete) {
        foreach (Entity* e, Aurasim::selectedEntities()) {
            if(!e->parentItem() && e->canRemove())
               e->removeSelf();
        }
    }

    if(event->modifiers() | Qt::ControlModifier) {
        switch (event->key()) {
        case Qt::Key_C:
        {
            QString cpText;
            QJsonArray entities;
            QPointF topLeft(10000000, -10000000);
            foreach(Entity* e, Aurasim::selectedEntities()) {
                if(!e->canCopy())
                    continue;
                topLeft = QPointF(qMin(topLeft.x(), e->x()), qMax(topLeft.y(), e->y()));
            }

            foreach(Entity* e, Aurasim::selectedEntities()) {
                if(!e->canCopy())
                    continue;
                QJsonObject obj;
                QPointF rPos = e->pos() - topLeft;
                obj.insert("name", e->name());
                obj.insert("x", rPos.x());
                obj.insert("y", rPos.y());
                entities.append(obj);
            }
            QJsonDocument doc(entities);
            cpText = doc.toJson(QJsonDocument::Indented);
            QGuiApplication::clipboard()->setText(cpText);
            break;
        }
        case Qt::Key_V:
        {
            QPoint pos = views().first()->mapFromGlobal(QCursor::pos());
            QPointF scenePos = views().first()->mapToScene(pos);

            QJsonDocument doc = QJsonDocument::fromJson(QGuiApplication::clipboard()->text().toLatin1());
            QJsonArray items = doc.array();
            foreach (const QJsonValue& item, items) {
                QJsonObject obj = item.toObject();
                Entity* e = Aurasim::newEntity(obj["name"].toString());
                if(e){
                    QPointF ePos(obj["x"].toVariant().toReal(), obj["y"].toVariant().toReal());
                    e->setPos(scenePos + ePos);
                    Aurasim::entityManager()->addEntity(e);
                }
            }
            break;
        }
        default:
            break;
        }
    }
}
